//
//  LeanIMManager.m
//  MLIM
//
//  Created by molon on 15/7/2.
//  Copyright (c) 2015年 molon. All rights reserved.
//

#import "LeanIMManager.h"
#import <AVOSCloudIM/AVOSCloudIM.h>
#import "IMCommon.h"
#import <AVOSCloud/AVOSCloud.h>

#define IMCLIENT ([AVIMClient defaultClient])
#define IMCM_CURRENTID_CHECK if ([NSString IsNilOrEmpty:IMCM.currentClientId]) { return;}

#define kRetryInterval 10.0f
typedef void (^AVIMConversationResultBlock)(AVIMConversation *conv, NSError *error);

@interface IMManager(Subclass)

@property (nonatomic, assign) IMManagerStatus status;

@end

@interface LeanIMManager()<AVIMClientDelegate>
@end

@implementation LeanIMManager

- (instancetype)init
{
    self = [super init];
    if (self) {
        IMCLIENT.delegate = self;
        [self updateStatus];
    }
    return self;
}

#pragma mark - notification
- (void)appLaunchedWithAVOSApplicationId:(NSString*)avosApplicationId AVOSClientKey:(NSString*)avosClientKey
{
#ifdef DEBUG
    //    [AVOSCloud setVerbosePolicy:kAVVerboseShow];
    //    [AVLogger addLoggerDomain:AVLoggerDomainIM];
    //    [AVLogger addLoggerDomain:AVLoggerDomainCURL];
//        [AVLogger setLoggerLevelMask:AVLoggerLevelAll];
    
//    [AVOSCloud setAllLogsEnabled:YES];
#endif
    [AVOSCloud setApplicationId:avosApplicationId clientKey:avosClientKey];
    
    //设置一些测试的manager 回调
    [IMCM setSendIMMessageBlock:^(IMMessage *message, IMSendMessageCallbackBlock callback) {
        NSAssert(message.conversationId, @"IMMessage 必须要有其会话ID");
        [self convWithIMConversationSearchType:IMConversationSearchTypeConvId object:message.conversationId callback:^(AVIMConversation *conv, NSError *error) {
            if (conv) {
                AVIMMessage *avMsg = [LeanIMManager avMessageWithIMMessage:message];
                NSAssert(avMsg, @"AVIMMessage必须有效");
                [conv sendMessage:avMsg callback:^(BOOL succeeded, NSError *error) {
                    message.messageId = avMsg.messageId;
                    if (callback) {
                        callback(message,succeeded,error);
                    }
                }];
            }
        }];
    }];
    
    [IMCM setImConversationSearchBlock:^(IMConversationSearchType type, id object, IMConversationSearchCallbackBlock callback) {
        [self convWithIMConversationSearchType:type object:object callback:^(AVIMConversation *conv, NSError *error) {
            if (callback) {
                callback([LeanIMManager imConvWithAVConversation:conv],error);
            }
        }];
    }];
    
    [IMCM setImMessagesBlock:^(NSString *imConvId, IMMessage *beforeIMMessage, NSInteger count, IMArrayResultBlock callback) {
        [self convWithIMConversationSearchType:IMConversationSearchTypeConvId object:imConvId callback:^(AVIMConversation *conv, NSError *error) {
            if (!conv||error) {
                if (callback) {
                    callback(nil,error);
                }
                return;
            }
            
            IMArrayResultBlock preCallback = ^(NSArray *objects, NSError *error){
                if (callback) {
                    if (objects.count>0) {
                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                            NSMutableArray *imMessages = [NSMutableArray array];
                            for (AVIMMessage *avMsg in objects) {
                                if (![avMsg isKindOfClass:[AVIMFileMessage class]]) {
                                    [imMessages addObject:[LeanIMManager imMessageWithAVIMMessage:avMsg]];
                                }
                            }
                            dispatch_async(dispatch_get_main_queue(),^{
                                callback(imMessages,error);
                            });
                        });
                    }else{
                        callback(objects,error);
                    }
                }
            };
            
            //            DLOG(@"找寻%@以前的消息",beforeIMMessage.messageId);
            if (beforeIMMessage) {
                [conv queryMessagesBeforeId:beforeIMMessage.messageId timestamp:beforeIMMessage.sendTimestamp*1000 limit:count callback:preCallback];
            }else{
                [conv queryMessagesWithLimit:count callback:preCallback];
            }
        }];
    }];
    
    [IMCM setAlertBodyForLocalNotificationBlock:^NSString *(IMUser *user, IMMessage *message) {
        NSString *suffix = @"";
        if ([message isKindOfClass:[IMTextMessage class]]) {
            suffix = [NSString stringWithFormat:@": %@",CHILD(IMTextMessage, message).content];
        }else if ([message isKindOfClass:[IMImageMessage class]]){
            suffix = @"发来了一张图片";
        }else if ([message isKindOfClass:[IMImageMessage class]]){
            suffix = @"发来了一段语音";
        }
        
        NSString *tips = [NSString stringWithFormat:@"%@%@",user.name,suffix];
        if (tips.length>20) {
            tips = [[tips substringToIndex:20]stringByAppendingString:@"..."];
        }
        return tips;
    }];
}

#pragma mark - private call
- (void)createConversationWithMembers:(NSArray*)memberIds callback:(AVIMConversationResultBlock)callback
{
    NSAssert(memberIds.count>=2, @"memberIds数量必须大于2");
    
    NSString *name = @"";
    for (NSString *clientId in memberIds) {
        name = [name stringByAppendingFormat:@"%@-",clientId];
    }
    name = [name substringToIndex:name.length-1];
    
    [IMCLIENT createConversationWithName:name clientIds:memberIds callback:^(AVIMConversation *conversation, NSError *error) {
        if (error) {
            //直接重试连接
            [self connectWithCallback:nil];
        }
        
        if (callback) {
            callback(conversation,error);
        }
    }];
}

- (void)convWithIMConversationSearchType:(IMConversationSearchType)type object:(id)object
                                callback:(AVIMConversationResultBlock)callback
{
    AVIMConversationQuery *q = [IMCLIENT conversationQuery];
    q.limit = 1;
    if (type==IMConversationSearchTypeConvId) {
        AVIMConversation *conv = [IMCLIENT conversationForId:object];
        //这里是因为，经过测试。如果这个会话未经过query查询，而是通过receive message回调第一次拿过的话，
        //通过conversationForId也是查的到的，但是除了conversationId，其他属性都是nil，所以在此当作其无效吧，重新query
        if (conv.members.count>0&&conv.name.length>0) {
            if (callback) callback(conv,nil);
            return;
        }
        
        [q whereKey:kAVIMKeyConversationId equalTo:object];
    }else{
        [q whereKey:kAVIMKeyMember containsAllObjectsInArray:object];
    }
    [q findConversationsWithCallback: ^(NSArray *objects, NSError *error) {
        if (objects.count<=0&&type==IMConversationSearchTypeMemberIds) {
            //新建这个会话
            [self createConversationWithMembers:object callback:^(AVIMConversation *conv, NSError *error) {
                if (callback) callback(conv,error);
            }];
            return;
        }
        if (callback) callback([objects firstObject],error);
    }];
}

- (void)queryRecentConversationsWithLimit:(NSInteger)limit callback:(IMArrayResultBlock)callback
{
    NSAssert(limit<=1000, @"limit不可大于1000");
    NSAssert(IMCM.currentClientId, @"IM没登录不可query");
    
    AVIMConversationQuery *q = [IMCLIENT conversationQuery];
    q.limit = limit;
    [q whereKey:kAVIMKeyMember containsAllObjectsInArray:@[IMCM.currentClientId]];
    [q whereKey:@"lm" greaterThan:[NSDate dateWithTimeIntervalSince1970:0]];
    [q orderByDescending:@"lm"];
    [q findConversationsWithCallback:^(NSArray *objects, NSError *error) {
        if (callback) {
            callback(objects,error);
        }
    }];
}

#pragma mark - model turn
+ (IMConversation*)imConvWithAVConversation:(AVIMConversation*)conv
{
    if (!conv) {
        return nil;
    }
    
    IMConversation *imConv = [IMConversation new];
    imConv.conversationId = conv.conversationId;
    imConv.clientIds = conv.members;
    imConv.lastMessageTime = [conv.lastMessageAt timeIntervalSince1970];
    
    return imConv;
}

+ (AVIMMessage*)avMessageWithIMMessage:(IMMessage*)imMsg
{
    if (!imMsg) {
        return nil;
    }
    AVIMMessage *msg = nil;
    if ([imMsg isKindOfClass:[IMTextMessage class]]) {
        msg = [AVIMTextMessage messageWithText:CHILD(IMTextMessage, imMsg).content attributes:nil];
    }else if ([imMsg isKindOfClass:[IMImageMessage class]]){
        msg = [AVIMImageMessage messageWithText:nil attachedFilePath:[CHILD(IMImageMessage, imMsg).imageURL path] attributes:nil];
    }else if ([imMsg isKindOfClass:[IMAudioMessage class]]){
        msg = [AVIMAudioMessage messageWithText:nil attachedFilePath:[CHILD(IMAudioMessage, imMsg).audioURL path] attributes:@{@"duration":@(CHILD(IMAudioMessage, imMsg).duration)}];
    }
    return msg;
}

+ (IMMessage*)imMessageWithAVIMMessage:(AVIMMessage*)avMsg
{
    if (!avMsg) {
        return nil;
    }
    IMMessage *imMsg = nil;
    if ([avMsg isKindOfClass:[AVIMTypedMessage class]]) {
        if (CHILD(AVIMTypedMessage, avMsg).mediaType == kAVIMMessageMediaTypeImage) {
            IMImageMessage *msg = [IMImageMessage new];
            msg.imageURL = [NSURL URLWithString:CHILD(AVIMImageMessage, avMsg).file.url];
            msg.imageHeight = CHILD(AVIMImageMessage, avMsg).height;
            msg.imageWidth = CHILD(AVIMImageMessage, avMsg).width;
            imMsg = msg;
        }else if (CHILD(AVIMTypedMessage, avMsg).mediaType == kAVIMMessageMediaTypeAudio) {
            IMAudioMessage *msg = [IMAudioMessage new];
            msg.audioURL = [NSURL URLWithString:CHILD(AVIMAudioMessage, avMsg).file.url];
            msg.duration = [CHILD(AVIMAudioMessage, avMsg).attributes[@"duration"] doubleValue];
            imMsg = msg;
        }else if (CHILD(AVIMTypedMessage, avMsg).mediaType == kAVIMMessageMediaTypeText) {
            NSString *text = CHILD(AVIMTextMessage, avMsg).text;
            if ([text hasPrefix:@"!@#="]) { //新的协议，之前那个有点问题，lean煞笔拉取历史记录时候不按原格式来
                //认作是订单消息吧
                id attrs = [[text substringFromIndex:4]objectFromJSONString];
                if ([attrs isKindOfClass:[NSDictionary class]]&&attrs[@"dataType"]&&[attrs[@"dataType"] isEqualToString:@"dispatch"]) {
                    IMCustomMessage *msg = [IMCustomMessage new];
                    msg.object = attrs;
                    imMsg = msg;
                }
            }
            
            if (!imMsg) {
                id content = [avMsg.content objectFromJSONString];
                if ([content isKindOfClass:[NSDictionary class]]) {
                    NSDictionary *attrs = content[@"_lcattrs"];
                    if (attrs[@"dataType"]&&[attrs[@"dataType"] isEqualToString:@"dispatch"]) {
                        //TODO: 这里先限制死吧，这样的话以后加其他type的时候会走到text转换那里，不至于崩溃
                        IMCustomMessage *msg = [IMCustomMessage new];
                        msg.object = attrs;
                        imMsg = msg;
                    }
                    //                else if ([CHILD(AVIMTextMessage, avMsg).text isEqualToString:@"有10个人接受了您的订单"]){
                    //                    _po(attrs);
                    //                }
                }
            }
            
            if (!imMsg) {
                IMTextMessage *msg = [IMTextMessage new];
                msg.content = CHILD(AVIMTextMessage, avMsg).text;
                imMsg = msg;
            }
        }else{
            IMTextMessage *msg = [IMTextMessage new];
            msg.content = @"您的版本不支持此类型的消息";
            imMsg = msg;
        }
    }else if ([avMsg isMemberOfClass:[AVIMMessage class]]){
        IMTextMessage *msg = [IMTextMessage new];
        msg.content = @"您的版本不支持此类型的消息";
        imMsg = msg;
    }
    
    //设置一些基本的发送者还是接收者的七七八八的。
    imMsg.clientId = avMsg.clientId;
    imMsg.messageId = avMsg.messageId;
    imMsg.conversationId = avMsg.conversationId;
    imMsg.sendTimestamp = (double)avMsg.sendTimestamp/1000;
    
    return imMsg;
}

#pragma mark - helper
- (void)updateStatus
{
    if ([NSString IsNilOrEmpty:IMCM.currentClientId]) {
        self.status = IMManagerStatusNone;
        return;
    }
    
    //关闭关闭中None都认作未连接吧
    if (IMCLIENT.status == AVIMClientStatusClosed||IMCLIENT.status == AVIMClientStatusNone||IMCLIENT.status == AVIMClientStatusClosing||IMCLIENT.status == AVIMClientStatusPaused||![IMCLIENT.clientId isEqualToString:IMCM.currentClientId]) {
        self.status = IMManagerStatusPaused;
        return;
    }
    
    if (IMCLIENT.status == AVIMClientStatusOpened) {
        self.status = IMManagerStatusConnected;
    }else if (IMCLIENT.status == AVIMClientStatusResuming||IMCLIENT.status == AVIMClientStatusOpening) {
        //开启和连接中我们都认作是连接中
        self.status = IMManagerStatusConnecting;
    }
}

#pragma mark - outcall
- (void)loginWithClientId:(NSString*)clientId callback:(IMBooleanResultBlock)callback
{
    NSAssert(![NSString IsNilOrEmpty:clientId], @"clietnId不可为空");
    
    //切换用户
    [IMCM resetWithCurrentClientId:clientId];
    //更新状态
    self.status = IMManagerStatusPaused;
    //连接lean
    [self connectWithCallback:callback];
}

//主动关闭连接
- (void)logoutWithCallback:(IMBooleanResultBlock)callback
{
    //直接清除
    [IMCM clean];
    //更新状态
    [self updateStatus];
    
    if (IMCLIENT.status != AVIMClientStatusClosed&&IMCLIENT.status != AVIMClientStatusNone) {
        [IMCLIENT closeWithCallback:^(BOOL succeeded, NSError *error) {
            //在关闭过程中又登录了，需要重新请求登录
            if (![NSString IsNilOrEmpty:IMCM.currentClientId]){
                if (IMCLIENT.status == AVIMClientStatusClosed||IMCLIENT.status == AVIMClientStatusNone) {
                    [self connectWithCallback:nil];
                }
                if (callback) {
                    callback(NO,error);
                }
                return;
            }
            
            //失败是否需要重试
            if (callback) {
                callback(succeeded,error);
            }
            return;
        }];
    }else{
        if (callback) {
            callback(YES,nil);
        }
    }
}

#pragma mark - helper
//连接到lean
- (void)connectWithCallback:(IMBooleanResultBlock)callback
{
    if ([NSString IsNilOrEmpty:IMCM.currentClientId]) {
        if (callback) {
            callback(NO,nil);
        }
        return;
    }
    
    //如果已经连接了就不需要连接了
    if (self.status==IMManagerStatusConnected) {
        if (callback) {
            callback(YES,nil);
        }
        return;
    }
    
    void(^connectBlock)() = ^{
        //当前imclient没连接，尝试连接
        self.status = IMManagerStatusConnecting;
        [IMCLIENT openWithClientId:IMCM.currentClientId callback:^(BOOL succeeded, NSError *error) {
            if ([NSString IsNilOrEmpty:IMCM.currentClientId]){
                //这时候说明在open过程中做过注销操作。那就再次进行关闭
                if (IMCLIENT.status != AVIMClientStatusClosed&&IMCLIENT.status != AVIMClientStatusNone) {
                    [IMCLIENT closeWithCallback:nil];
                }
                if (callback) {
                    callback(NO,nil);
                }
                return;
            }
            
            
            //这个东西现在保存没调用，留以后保存，这里设置是为了防止有多个channel没清理干净，他们SDK的BUG，默认应当以某clientId连接之后lean就会直接更新channel
            [[AVInstallation currentInstallation]setChannels:@[IMCM.currentClientId]];
            
            
            //如果这会发现登录的和想要登录的currentClientId不一致，那也认为没成功
            if (succeeded&&![IMCLIENT.clientId isEqualToString:IMCM.currentClientId]) {
                succeeded = NO;
            }
            
            [self updateStatus];
            
            if (succeeded){
                /* 在这里query一下的好处是
                 1. 进到最近已有的那些会话里就不会重新query了。使用conversationForId就直接能找到有效的
                 2. 在切换了设备后，之前最近会话也能及时的刷出来
                 3. 更新最后一条消息，这样在会话列表页面显示的kMLIM_MaxDisplayConvCount数量会话的排序能及时的体现。同时也保证了第2点情况下，列表的显示(因为我们的列表里认作没有最后一条消息的会话是不应当显示的)
                 4. 说白了就是尽量做一下和lean的数据同步
                 */
                //由于lean没有提供良好的支持直接会话查询带回最新消息的接口，保证不了第三点
                [self queryRecentConversationsWithLimit:kMLIM_MaxBackupConvCount callback:^(NSArray *objects, NSError *error) {
                    NSMutableArray *imConvs = [NSMutableArray arrayWithCapacity:objects.count];
                    for (AVIMConversation *avIMCov in objects) {
                        [imConvs addObject:[LeanIMManager imConvWithAVConversation:avIMCov]];
                    }
                    [IMCM updateOrAddIMConversations:imConvs callback:nil];
                }];
                
            }else{
                //每隔kRetryInterval秒重试连接
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kRetryInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    [self connectWithCallback:nil];
                });
            }
            if (callback) {
                callback(succeeded,error);
            }
        }];
    };
    
    if (IMCLIENT.status == AVIMClientStatusNone||IMCLIENT.status == AVIMClientStatusClosed) {
        connectBlock();
    }else{
        //关闭当前连接
        [IMCLIENT closeWithCallback: ^(BOOL succeeded, NSError *error) {
            if (succeeded) {
                connectBlock();
            }else{
                //每隔kRetryInterval秒重试
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kRetryInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    [self connectWithCallback:nil];
                });
                
                if (callback) {
                    callback(NO,error);
                }
            }
        }];
    }
    
}

#pragma mark - delegate
/*!
 当前聊天状态被暂停，常见于网络断开时触发。
 */
- (void)imClientPaused:(AVIMClient *)imClient
{
    IMCM_CURRENTID_CHECK
    
    self.status = IMManagerStatusPaused;
}

/*!
 当前聊天状态开始恢复，常见于网络断开后开始重新连接。
 */
- (void)imClientResuming:(AVIMClient *)imClient
{
    IMCM_CURRENTID_CHECK
    
    self.status = IMManagerStatusConnecting;
}

/*!
 当前聊天状态已经恢复，常见于网络断开后重新连接上。
 */
- (void)imClientResumed:(AVIMClient *)imClient
{
    IMCM_CURRENTID_CHECK
    
    [self updateStatus];
}

/*!
 接收到新的普通消息。
 @param conversation － 所属对话
 @param message - 具体的消息
 @return None.
 */
- (void)conversation:(AVIMConversation *)conversation didReceiveCommonMessage:(AVIMMessage *)message
{
    IMCM_CURRENTID_CHECK
    
    MARK
    
    IMMessage *imMsg = [LeanIMManager imMessageWithAVIMMessage:message];
    if (imMsg) {
        [IMCM addNewIMMessages:@[imMsg] toIMConvId:imMsg.conversationId];
    }
}

/*!
 接收到新的富媒体消息。
 @param conversation － 所属对话
 @param message - 具体的消息
 @return None.
 */
- (void)conversation:(AVIMConversation *)conversation didReceiveTypedMessage:(AVIMTypedMessage *)message
{
    IMCM_CURRENTID_CHECK
    
    MARK
    IMMessage *imMsg = [LeanIMManager imMessageWithAVIMMessage:message];
    if (imMsg) {
        [IMCM addNewIMMessages:@[imMsg] toIMConvId:imMsg.conversationId];
    }
}

/*!
 对话中有新成员加入的通知。
 @param conversation － 所属对话
 @param clientIds - 加入的新成员列表
 @param clientId - 邀请者的 id
 @return None.
 */
- (void)conversation:(AVIMConversation *)conversation membersAdded:(NSArray *)clientIds byClientId:(NSString *)clientId
{
    MARK
}
/*!
 对话中有成员离开的通知。
 @param conversation － 所属对话
 @param clientIds - 离开的成员列表
 @param clientId - 操作者的 id
 @return None.
 */
- (void)conversation:(AVIMConversation *)conversation membersRemoved:(NSArray *)clientIds byClientId:(NSString *)clientId
{
    MARK
}

/*!
 被邀请加入对话的通知。
 @param conversation － 所属对话
 @param clientId - 邀请者的 id
 @return None.
 */
- (void)conversation:(AVIMConversation *)conversation invitedByClientId:(NSString *)clientId
{
    MARK
}

/*!
 从对话中被移除的通知。
 @param conversation － 所属对话
 @param clientId - 操作者的 id
 @return None.
 */
- (void)conversation:(AVIMConversation *)conversation kickedByClientId:(NSString *)clientId
{
    MARK
}

@end
